﻿using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using UnityEngine.TextCore.Text;
using UnityEngine.UIElements;
using Cursor = UnityEngine.UIElements.Cursor;

namespace winS.UnityEditor.UIElement
{
    public static class IStyleExtension
    {
        private static PropertyInfo defaultCursorIdProperty;

        public static void SetCursor(this IStyle style, MouseCursor mouseCursor)
        {
            Cursor cursor = new Cursor();
            defaultCursorIdProperty ??= typeof(Cursor).GetProperty("defaultCursorId", BindingFlags.Instance | BindingFlags.NonPublic);
            defaultCursorIdProperty.SetValue(cursor, (int)mouseCursor);
            style.cursor = new StyleCursor(cursor);
        }

        public static void SetTop(this IStyle style, float top, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            style.top = new StyleLength(new Length(top, lengthUnit));
        }
        public static void SetLeft(this IStyle style, float left, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            style.left = new StyleLength(new Length(left, lengthUnit));
        }
        public static void SetRight(this IStyle style, float right, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            style.right = new StyleLength(new Length(right, lengthUnit));
        }
        public static void SetBottom(this IStyle style, float bottom, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            style.bottom = new StyleLength(new Length(bottom, lengthUnit));
        }

        public static void SetSize(this IStyle style, float size, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            SetWidth(style, size, lengthUnit);
            SetHeight(style, size, lengthUnit);
        }
        public static void SetSize(this IStyle style, Vector2 size, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            SetWidth(style, size.x, lengthUnit);
            SetHeight(style, size.y, lengthUnit);
        }
        public static void SetWidth(this IStyle style, float width, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            style.width = new StyleLength(new Length(width, lengthUnit));
        }
        public static void SetHeight(this IStyle style, float height, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            style.height = new StyleLength(new Length(height, lengthUnit));
        }

        public static void SetMaxWidth(this IStyle style, float maxWidth, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            style.maxWidth = new StyleLength(new Length(maxWidth, lengthUnit));
        }
        public static void SetMaxHeight(this IStyle style, float maxHeight, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            style.maxHeight = new StyleLength(new Length(maxHeight, lengthUnit));
        }

        public static void SetMinWidth(this IStyle style, float minWidth, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            style.minWidth = new StyleLength(new Length(minWidth, lengthUnit));
        }
        public static void SetMinHeight(this IStyle style, float minHeight, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            style.minHeight = new StyleLength(new Length(minHeight, lengthUnit));
        }

        public static void SetFixedWidth(this IStyle style, float width, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            style.minWidth = style.maxWidth = new StyleLength(new Length(width, lengthUnit));
        }
        public static void SetFixedHeight(this IStyle style, float height, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            style.minHeight = style.maxHeight = new StyleLength(new Length(height, lengthUnit));
        }

        public static void SetFlexBasis(this IStyle style, float flexBasis, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            style.flexBasis = new StyleLength(new Length(flexBasis, lengthUnit));
        }

        public static void SetOpacity(this IStyle style, float opacity)
        {
            style.opacity = opacity;
        }

        public static void SetScale(this IStyle style, Vector2 scale)
        {
            style.scale = new Scale(new Vector3(scale.x, scale.y, 1f));
        }
        public static void SetScale(this IStyle style, float scale)
        {
            style.scale = new Scale(new Vector3(scale, scale, 1f));
        }
        public static void SetScale(this IStyle style, float x, float y)
        {
            style.scale = new Scale(new Vector3(x, y, 1f));
        }

        public static void SetMargin(this IStyle style, float value)
        {
            style.marginBottom = style.marginTop = style.marginLeft = style.marginRight = value;
        }
        public static void SetRotate(this IStyle style, float rotate)
        {
            style.rotate = new Rotate(rotate);
        }
        public static void SetPadding(this IStyle style, float value)
        {
            style.paddingBottom = style.paddingTop = style.paddingLeft = style.paddingRight = value;
        }

        public static void SetVerticalMargin(this IStyle style, float value, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            style.marginBottom = style.marginTop = new StyleLength(new Length(value, lengthUnit));
        }
        public static void SetHorizontalMargin(this IStyle style, float value, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            style.marginLeft = style.marginRight = new StyleLength(new Length(value, lengthUnit));
        }
        public static void SetVerticalPadding(this IStyle style, float value, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            style.paddingBottom = style.paddingTop = new StyleLength(new Length(value, lengthUnit));
        }
        public static void SetHorizontalPadding(this IStyle style, float value, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            style.paddingLeft = style.paddingRight = new StyleLength(new Length(value, lengthUnit));
        }

        public static void SetMarginTop(this IStyle style, float marginTop, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            style.marginTop = new StyleLength(new Length(marginTop, lengthUnit));
        }
        public static void SetMarginLeft(this IStyle style, float marginLeft, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            style.marginLeft = new StyleLength(new Length(marginLeft, lengthUnit));
        }
        public static void SetMarginRight(this IStyle style, float marginRight, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            style.marginRight = new StyleLength(new Length(marginRight, lengthUnit));
        }
        public static void SetMarginBottom(this IStyle style, float marginBottom, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            style.marginBottom = new StyleLength(new Length(marginBottom, lengthUnit));
        }

        public static void SetPaddingTop(this IStyle style, float paddingTop, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            style.paddingTop = new StyleLength(new Length(paddingTop, lengthUnit));
        }
        public static void SetPaddingLeft(this IStyle style, float paddingLeft, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            style.paddingLeft = new StyleLength(new Length(paddingLeft, lengthUnit));
        }
        public static void SetPaddingRight(this IStyle style, float paddingRight, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            style.paddingRight = new StyleLength(new Length(paddingRight, lengthUnit));
        }
        public static void SetPaddingBottom(this IStyle style, float paddingBottom, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            style.paddingBottom = new StyleLength(new Length(paddingBottom, lengthUnit));
        }

        public static void SetBorder(this IStyle style, Color color, float width)
        {
            SetBorderColor(style, color);
            SetBorderWidth(style, width);
        }
        public static void SetBorder(this IStyle style, Color color, float width, float radius)
        {
            SetBorderColor(style, color);
            SetBorderWidth(style, width);
            SetBorderRadius(style, radius);
        }

        public static void SetVerticalBorder(this IStyle style, Color color, float width)
        {
            style.borderTopWidth = style.borderBottomWidth = width;
            style.borderTopColor = style.borderBottomColor = color;
        }
        public static void SetHorizontalBorder(this IStyle style, Color color, float width)
        {
            style.borderLeftWidth = style.borderRightWidth = width;
            style.borderLeftColor = style.borderRightColor = color;
        }

        public static void SetBorderColor(this IStyle style, Color color)
        {
            style.borderBottomColor = style.borderTopColor = style.borderLeftColor = style.borderRightColor = new StyleColor(color);
        }
        public static void SetBorderColor(this IStyle style, byte r, byte g, byte b, byte a = 255)
        {
            SetBorderColor(style, new Color32(r, g, b, a));
        }

        public static void SetBorderWidth(this IStyle style, float width)
        {
            style.borderBottomWidth = style.borderTopWidth = style.borderLeftWidth = style.borderRightWidth = width;
        }
        public static void SetBorderRadius(this IStyle style, float radius, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            style.borderBottomLeftRadius = style.borderTopLeftRadius = style.borderTopRightRadius = style.borderBottomRightRadius = new StyleLength(new Length(radius, lengthUnit));
        }

        public static void SetBorderTop(this IStyle style, float width, Color color)
        {
            SetBorderTopWdith(style, width);
            SetBorderTopColor(style, color);
        }
        public static void SetBorderLeft(this IStyle style, float width, Color color)
        {
            SetBorderLeftWdith(style, width);
            SetBorderLeftColor(style, color);
        }
        public static void SetBorderRight(this IStyle style, float width, Color color)
        {
            SetBorderRightWdith(style, width);
            SetBorderRightColor(style, color);
        }
        public static void SetBorderBottom(this IStyle style, float width, Color color)
        {
            SetBorderBottomWdith(style, width);
            SetBorderBottomColor(style, color);
        }

        public static void ClearBorderTop(this IStyle style)
        {
            SetBorderTop(style, 0f, Color.clear);
        }
        public static void ClearBorderLeft(this IStyle style)
        {
            SetBorderLeft(style, 0f, Color.clear);
        }
        public static void ClearBorderRight(this IStyle style)
        {
            SetBorderRight(style, 0f, Color.clear);
        }
        public static void ClearBorderBottom(this IStyle style)
        {
            SetBorderBottom(style, 0f, Color.clear);
        }

        public static void SetBorderTop(this IStyle style, float width, float radius, Color color)
        {
            SetBorderTopWdith(style, width);
            SetBorderTopColor(style, color);
            SetBorderTopRadius(style, radius);
        }
        public static void SetBorderLeft(this IStyle style, float width, float radius, Color color)
        {
            SetBorderLeftWdith(style, width);
            SetBorderLeftColor(style, color);
            SetBorderLeftRadius(style, radius);
        }
        public static void SetBorderRight(this IStyle style, float width, float radius, Color color)
        {
            SetBorderRightWdith(style, width);
            SetBorderRightColor(style, color);
            SetBorderRightRadius(style, radius);
        }
        public static void SetBorderBottom(this IStyle style, float width, float radius, Color color)
        {
            SetBorderBottomWdith(style, width);
            SetBorderBottomColor(style, color);
            SetBorderBottomRadius(style, radius);
        }

        public static void SetBorderTopColor(this IStyle style, Color color)
        {
            style.borderTopColor = new StyleColor(color);
        }
        public static void SetBorderLeftColor(this IStyle style, Color color)
        {
            style.borderLeftColor = new StyleColor(color);
        }
        public static void SetBorderRightColor(this IStyle style, Color color)
        {
            style.borderRightColor = new StyleColor(color);
        }
        public static void SetBorderBottomColor(this IStyle style, Color color)
        {
            style.borderBottomColor = new StyleColor(color);
        }

        public static void SetBorderTopColor(this IStyle style, byte r, byte g, byte b, byte a = 255)
        {
            SetBorderTopColor(style, new Color32(r, g, b, a));
        }
        public static void SetBorderLeftColor(this IStyle style, byte r, byte g, byte b, byte a = 255)
        {
            SetBorderLeftColor(style, new Color32(r, g, b, a));
        }
        public static void SetBorderRightColor(this IStyle style, byte r, byte g, byte b, byte a = 255)
        {
            SetBorderRightColor(style, new Color32(r, g, b, a));
        }
        public static void SetBorderBottomColor(this IStyle style, byte r, byte g, byte b, byte a = 255)
        {
            SetBorderBottomColor(style, new Color32(r, g, b, a));
        }

        public static void SetBorderTopRadius(this IStyle style, float radius, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            style.borderTopLeftRadius = style.borderTopRightRadius = new StyleLength(new Length(radius, lengthUnit));
        }
        public static void SetBorderLeftRadius(this IStyle style, float radius, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            style.borderBottomLeftRadius = style.borderTopLeftRadius = new StyleLength(new Length(radius, lengthUnit));
        }
        public static void SetBorderRightRadius(this IStyle style, float radius, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            style.borderBottomRightRadius = style.borderTopRightRadius = new StyleLength(new Length(radius, lengthUnit));
        }
        public static void SetBorderBottomRadius(this IStyle style, float radius, LengthUnit lengthUnit = LengthUnit.Pixel)
        {
            style.borderBottomLeftRadius = style.borderBottomRightRadius = new StyleLength(new Length(radius, lengthUnit));
        }

        public static void SetBorderWdith(this IStyle style, float width)
        {
            style.borderTopWidth = style.borderLeftWidth = style.borderRightWidth = style.borderBottomWidth = width;
        }
        public static void SetBorderTopWdith(this IStyle style, float width)
        {
            style.borderTopWidth = width;
        }
        public static void SetBorderLeftWdith(this IStyle style, float width)
        {
            style.borderLeftWidth = width;
        }
        public static void SetBorderRightWdith(this IStyle style, float width)
        {
            style.borderRightWidth = width;
        }
        public static void SetBorderBottomWdith(this IStyle style, float width)
        {
            style.borderBottomWidth = width;
        }

        public static void SetColor(this IStyle style, Color color)
        {
            style.color = color;
        }
        public static void SetColor(this IStyle style, byte r, byte g, byte b, byte a = 255)
        {
            SetColor(style, new Color32(r, g, b, a));
        }

        public static void SetBackgroundColor(this IStyle style, Color color)
        {
            style.backgroundColor = color;
        }
        public static void SetBackgroundColor(this IStyle style, byte r, byte g, byte b, byte a = 255)
        {
            SetBackgroundColor(style, new Color32(r, g, b, a));
        }

        public static void SetTransformOrigin(this IStyle style, Vector2 pivot)
        {
            style.transformOrigin = new TransformOrigin(new Length(pivot.x * 100f, LengthUnit.Percent), new Length(pivot.y * 100f, LengthUnit.Percent), 0f);
        }
        public static void SetTransformOrigin(this IStyle style, float x, float y)
        {
            style.transformOrigin = new TransformOrigin(new Length(x * 100f, LengthUnit.Percent), new Length(y * 100f, LengthUnit.Percent), 0f);
        }

        public static void SetDisplay(this IStyle style, bool display)
        {
            style.display = display ? DisplayStyle.Flex : DisplayStyle.None;
        }
        public static void SetFontStyle(this IStyle style, FontStyle fontStyle)
        {
            style.unityFontStyleAndWeight = new StyleEnum<FontStyle>(fontStyle);
        }
        public static void SetUnityFontDefinition(this IStyle style, FontAsset fontAsset)
        {
            style.unityFontDefinition = new StyleFontDefinition(fontAsset);
        }

        public static void SetTransition(this IStyle style, string name, float duration)
        {
            SetTransitionDuration(style, CheckPropertyName(style, name), duration);
        }
        public static void SetTransition(this IStyle style, string name, float duration, float delay)
        {
            int index = CheckPropertyName(style, name);
            SetTransitionDuration(style, index, duration);
            SetTransitionDelay(style, index, delay);
        }
        public static void SetTransition(this IStyle style, string name, EasingMode easingMode)
        {
            SetTransitionTimingFunction(style, CheckPropertyName(style, name), easingMode);
        }
        public static void SetTransition(this IStyle style, string name, float duration, EasingMode easingMode)
        {
            int index = CheckPropertyName(style, name);
            SetTransitionTimingFunction(style, index, easingMode);
            SetTransitionDuration(style, index, duration);
        }
        public static void SetTransition(this IStyle style, string name, float duration, float delay, EasingMode easingMode)
        {
            int index = CheckPropertyName(style, name);
            SetTransitionTimingFunction(style, index, easingMode);
            SetTransitionDuration(style, index, duration);
            SetTransitionDelay(style, index, delay);
        }

        private static int CheckPropertyName(IStyle style, string name)
        {
            int index = -1;
            if (style.transitionProperty.value != null)
            {
                index = style.transitionProperty.value.FindIndex(0, propertyName => propertyName.ToString() == name);
                if (index == -1) style.transitionProperty.value.Add(name);
            }
            else style.transitionProperty = new List<StylePropertyName>() { name };
            return index;
        }
        private static void SetTransitionDelay(IStyle style, int propertyIndex, float delay)
        {
            if (propertyIndex < 0)
            {
                if (style.transitionDelay.value != null) style.transitionDelay.value.Add(delay);
                else style.transitionDelay = new List<TimeValue>() { delay };
                return;
            }
            if (style.transitionDelay.value == null) throw new Exception($"{nameof(SetTransitionDelay)}: {nameof(IStyle.transitionDelay)} connot be null");
            List<TimeValue> newTransitionDelayList = new List<TimeValue>(style.transitionDelay.value);
            newTransitionDelayList[propertyIndex] = delay;
            style.transitionDelay = newTransitionDelayList;
        }
        private static void SetTransitionDuration(IStyle style, int propertyIndex, float duration)
        {
            if (propertyIndex < 0)
            {
                if (style.transitionDuration.value != null) style.transitionDuration.value.Add(duration);
                else style.transitionDuration = new List<TimeValue>() { duration };
                return;
            }
            if (style.transitionDuration.value == null) throw new Exception($"{nameof(SetTransitionDuration)}: {nameof(IStyle.transitionDuration)} connot be null");
            List<TimeValue> newTransitionDurationList = new List<TimeValue>(style.transitionDuration.value);
            newTransitionDurationList[propertyIndex] = duration;
            style.transitionDuration = newTransitionDurationList;
        }
        private static void SetTransitionTimingFunction(IStyle style, int propertyIndex, EasingMode easingMode)
        {
            if (propertyIndex < 0)
            {
                if (style.transitionTimingFunction.value != null) style.transitionTimingFunction.value.Add(easingMode);
                else style.transitionTimingFunction = new List<EasingFunction>() { easingMode };
                return;
            }
            if (style.transitionTimingFunction.value == null) throw new Exception($"{nameof(SetTransitionTimingFunction)}: {nameof(IStyle.transitionTimingFunction)} connot be null");
            List<EasingFunction> newTransitionTimingFunctionList = new List<EasingFunction>(style.transitionTimingFunction.value);
            newTransitionTimingFunctionList[propertyIndex] = easingMode;
            style.transitionTimingFunction = newTransitionTimingFunctionList;
        }
    }
}